/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFOfflineSource
  \brief to process offline data, an abstract base class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-09-28
  \date 2024-06-19 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-09-28   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <libgen.h>
#include <regex> // regular expression since c++11
#include "ETFRun.h"

#include "ETFOfflineSource.h"
#include "ETFParaManager.h"
#include "ETFPXIUnpacker.h"
#include "ETFVMEUnpacker.h"
#include "ETFReadShm.h"
#include "ETFMsg.h"

using std::cout;
using std::flush;
using std::regex;

#define er ETFMsg::Error

ETFOfflineSource::ETFOfflineSource() : ETFSource(), fRestOnlineSim(-1){}

ETFOfflineSource::~ETFOfflineSource(){
  Close();
} // dtor

// read event i
bool ETFOfflineSource::ReadEvent(unsigned i){
  unsigned j = 0;
  for(auto &m : fMap){
    m.first->Reset();
    while((j = m.first->GetOneEvent(m.second)) != UINT_MAX && j < i)
      cout << "Skipping event " << j << "\r" << flush;
    if(UINT_MAX == j) return false; // error happend, or eof reached
    if(j >= i){
      fUnpackGood = m.first->Unpack();
      fEventCnt++;
    } // end if
    // else ETFMsg::Warn("ETFOfflineSource", "ReadEvent: ev_start(%d) < minimal(%d)", i, j);
  } // end for

  // for online simulation
  if(fRestOnlineSim >= 0){
    // cout << "Event " << j << ", Press ENTER to continue..." << endl; getchar(); // DEBUG
    if(fRestOnlineSim) usleep(fRestOnlineSim);
    if(ETFMsg::sec() % 5 == 0) sleep(3);
  } // end if

  return true;
} // end member function ReadEvent

// pattern: d{8}_d{4}.dat; e.g. 20180708_0153.dat
bool ETFOfflineSource::IsPXIFile(const string &file, bool isStop){
  char name[128]; strcpy(name, file.data());
  bool result = regex_match(basename(name), regex("^\\d{8}_\\d{4}\\.dat$"));
  if(!result && isStop)
    er("ETFOfflineSource",
      "IsPXIFile: %s not of a PXI filename format (e.g. 20180708_0153.dat)", file.data());
  return result;
} // end membe function IsPXIFile

// pattern: d{8}.d{3}; e.g. 20180708.003
bool ETFOfflineSource::IsVMEFile(const string &file, bool isStop){
  char name[128]; strcpy(name, file.data());
  bool result = regex_match(basename(name), regex("^\\w+\\.\\d{3}$"));
  if(!result && isStop)
    er("ETFOfflineSource",
      "IsVMEFile: %s not of a VME filename format (e.g. 20180708.003)", file.data());
  return result;
} // end member function IsVMEFile

// set unpacker debug status
void ETFOfflineSource::SetDebug(bool opt){
  for(auto &m : fMap) m.first->SetDebug(opt);
} // end member function SetDebug

// close the input file
void ETFOfflineSource::Close(){
  for(auto &m : fMap){
    if(m.first) delete m.first;
    fclose(m.second);
  } // end for
} // end member function Close

void ETFOfflineSource::AddDataFile(const string &file){
  const char *f = file.data();
  if(access(f, F_OK)) er("ETFOfflineSource", "AddDataFile: %s not exists", f);
  
  FILE *fp = nullptr;
  if(!(fp = fopen(f, "rb"))) er("ETFOfflineSource", "AddDataFile: open %s failed", f);
  const DaqType t = IsPXIFile(file) ? kPXI : (IsVMEFile(file) ? kVME : kNDFDaqType);
  int id = 0;
  for(const auto &p : fMap) if(p.first->daqType() == t) id++;
  ETFUnpacker *u = nullptr;
  if(kPXI == t) u = new ETFPXIUnpacker(id);
  else if(kVME == t) u = new ETFVMEUnpacker(id);
  else er("ETFOfflineSource", "Unrecognized raw data file: %s", file.data());
  u->SetFileName(file);
  
  // register the unpacker
  fMap[u] = fp;
  fUpks.push_back(u);

  // assign root file name //
  if("" == fROOTFileName){
    char name[128]{}; strcpy(name, fMap.begin()->first->filename());
    const int runid = ETFParaManager::Instance()->GetRunId();
    if(runid < 0) fROOTFileName = string(basename(name)) + "_" + ETFMsg::time0(1) + ".root";
    else fROOTFileName = ETFMsg::Form("%s_%d.root", basename(name), runid);
  } // end if
} // end member function AddDataFile

// output to feed an online for test purposes
void ETFOfflineSource::OnlineSim(int rest_us){
  for(auto &p : fMap){
    auto u = p.first;
    char shm[128]{};
    if(0 == u->id()) sprintf(shm, "online_shm%s", u->pxi() ? "" : "_VME");
    else sprintf(shm, "online_shm%s%d", u->pxi() ? "" : "_VME", u->id());
    u->SetReadShm(new ETFReadShm(shm, u->pxi() ? kPXI : kVME, true));
  } // end for over unpackers
  fRestOnlineSim = rest_us;
} // end member functioin OnlineSim
